﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace WindowsGame2.Map
{
    class MapObject
    {
        RolePlayingGameData.Map m_mapData = null;
        ContentManager m_content = null;
        Point m_boundary = Point.Zero;
        Viewport m_viewport;
        CPathFinding m_pathFinding;
        ADJACENCY[] m_adjacency;
        int[][] m_dynamicObjectLayer;
        int[][] m_fog;

        public RolePlayingGameData.Map MapData
        {
            get { return m_mapData; }            
        }

        public Point Boundary
        {
            get { return m_boundary; }
        }

        public void Initialize(ContentManager content, Viewport viewport)
        {
            m_content = content;
            m_viewport = viewport;
        }

        public void LoadContent(string contentName)
        {
            contentName = Path.Combine(@"Maps", contentName);
            m_mapData = m_content.Load<RolePlayingGameData.Map>(contentName);
            m_boundary = new Point(m_mapData.TileSize.X * m_mapData.MapDimensions.X, m_mapData.TileSize.Y * m_mapData.MapDimensions.Y);
            m_dynamicObjectLayer = new int[m_mapData.MapDimensions.Y][];
            m_fog = new int[m_mapData.MapDimensions.Y][];

            int[][] tile_map = new int[m_mapData.MapDimensions.Y][];
            
            for (int y = 0; y < m_mapData.MapDimensions.Y; ++y)
            {
                m_dynamicObjectLayer[y] = new int[m_mapData.MapDimensions.X];
                m_fog[y] = new int[m_mapData.MapDimensions.X];

                tile_map[y] = new int[m_mapData.MapDimensions.X];
                for (int x = 0; x < m_mapData.MapDimensions.X; ++x)
                {
                    tile_map[y][x] = m_mapData.CollisionLayer[x + y * m_mapData.MapDimensions.X];
                    m_dynamicObjectLayer[y][x] = 0;
                    m_fog[y][x] = 0;
                }
            }

            m_pathFinding = new CPathFinding(tile_map, CPathFinding.MOVABLE_ADJANCENCY_TILE_COUNT.ADJANCENCY_COUNT_8WAY, 1);
            m_pathFinding.makeAdjacencyList(out m_adjacency, 1);
            
        }

        public void DrawBackground(SpriteBatch spriteBatch, Vector2 mapOriginPosition)
        {
            DrawLayers(spriteBatch, mapOriginPosition, true, true, false, false);
        }
        public void DrawMapObject(SpriteBatch spriteBatch, Vector2 mapOriginPosition)
        {
            DrawLayers(spriteBatch, mapOriginPosition, false, false, true, false);
        }

        public void DrawFog(SpriteBatch spriteBatch, Vector2 mapOriginPosition)
        {
            DrawLayers(spriteBatch, mapOriginPosition, false, false, false, true);
        }

        public void Update()
        {
            for (int y = 0; y < m_dynamicObjectLayer.Length; ++y)
            {
                for (int x = 0; x < m_dynamicObjectLayer[0].Length; ++x)
                {
                    m_dynamicObjectLayer[y][x] = 0;
                    m_fog[y][x] = Math.Max(m_fog[y][x]-1, 0);
                }
            }   
        }

        public void OccupiedTilePositionByDynamicObject(Point tilePosition, int sight)
        {
            m_dynamicObjectLayer[tilePosition.Y][tilePosition.X] += 1;
            for (int y = -sight; y <= sight; ++y)
            {
                for (int x = -sight; x <= sight; ++x)
                {
                    int destX = tilePosition.X + x;
                    int destY = tilePosition.Y + y;

                    if (destX < 0) continue;
                    if (destY < 0) continue;
                    if (m_mapData.MapDimensions.Y <= destY) continue;
                    if (m_mapData.MapDimensions.X <= destX) continue;
                    if (Math.Abs(Math.Round(Math.Cos(Math.Atan2(y, x)) * sight, 3)) < Math.Abs(x)) continue;
                    if (Math.Abs(Math.Round(Math.Sin(Math.Atan2(y, x)) * sight, 3)) < Math.Abs(y)) continue;
                   
                    m_fog[destY][destX] = 100;
                }
            }
        }

        public void UnoccupiedTilePositionByDynamicObject(Point tilePosition)
        {
            m_dynamicObjectLayer[tilePosition.Y][tilePosition.X] -= 1;
        }

        public bool IsOccupiedTilePositionByDynamicObject(Point tilePosition)
        {
            return m_dynamicObjectLayer[tilePosition.Y][tilePosition.X] > 0;
        }        

        bool CheckVisibility(Rectangle screenRectangle)
        {
            return ((screenRectangle.X > m_viewport.X - screenRectangle.Width) &&
                (screenRectangle.Y > m_viewport.Y - screenRectangle.Height) &&
                (screenRectangle.X < m_viewport.X + m_viewport.Width) &&
                (screenRectangle.Y < m_viewport.Y + m_viewport.Height));
        }

        void DrawLayers(SpriteBatch spriteBatch, Vector2 mapOriginPosition, bool drawBase, bool drawFringe, bool drawObject, bool drawFog)
        {
            // check the parameters
            if (spriteBatch == null)
            {
                throw new ArgumentNullException("spriteBatch");
            }
            if (!drawBase && !drawFringe && !drawObject && !drawFog)
            {
                return;
            }            

            Rectangle destinationRectangle =
                new Rectangle(0, 0, m_mapData.TileSize.X, m_mapData.TileSize.Y);

            for (int y = 0; y < m_mapData.MapDimensions.Y; y++)
            {
                for (int x = 0; x < m_mapData.MapDimensions.X; x++)
                {
                    destinationRectangle.X =
                        (int)mapOriginPosition.X + x * m_mapData.TileSize.X;
                    destinationRectangle.Y =
                        (int)mapOriginPosition.Y + y * m_mapData.TileSize.Y;

                    // If the tile is inside the screen
                    if (CheckVisibility(destinationRectangle))
                    {
                        Point mapPosition = new Point(x, y);
                        if (drawBase)
                        {
                            Rectangle sourceRectangle =
                                m_mapData.GetBaseLayerSourceRectangle(mapPosition);
                            if (sourceRectangle != Rectangle.Empty)
                            {
                                spriteBatch.Draw(m_mapData.Texture, destinationRectangle,
                                    sourceRectangle, Color.White);
                            }
                        }
                        if (drawFringe)
                        {
                            Rectangle sourceRectangle =
                                m_mapData.GetFringeLayerSourceRectangle(mapPosition);
                            if (sourceRectangle != Rectangle.Empty)
                            {
                                spriteBatch.Draw(m_mapData.Texture, destinationRectangle,
                                    sourceRectangle, Color.White);
                            }
                        }
                        if (drawObject)
                        {
                            Rectangle sourceRectangle =
                                m_mapData.GetObjectLayerSourceRectangle(mapPosition);
                            if (sourceRectangle != Rectangle.Empty)
                            {
                                spriteBatch.Draw(m_mapData.Texture, destinationRectangle,
                                    sourceRectangle, Color.White);
                            }
                        }
                        if (drawFog == true && m_fog[y][x] == 0)
                        {
                            Rectangle sourceRectangle = new Rectangle(8*64, 4*64, 64, 64);
                                
                            if (sourceRectangle != Rectangle.Empty)
                            {
                                spriteBatch.Draw(m_mapData.Texture, destinationRectangle,
                                    sourceRectangle, Color.White);
                            }
                        }
                    }
                }
            }
        }

        public bool DoPathFinding(Point startTilePosition, Point goalTilePosition, out List<TILE_POSITION> waypointContainer)
        {
            bool canWalk = m_pathFinding.getPathForGoalWithAStarSearch(
                    new TILE_POSITION(startTilePosition.X, startTilePosition.Y)
                , new TILE_POSITION(goalTilePosition.X, goalTilePosition.Y)
                , m_adjacency
                , ref m_dynamicObjectLayer
                , out waypointContainer);

            return canWalk;
        }
    }
}
